Skip to content

🐛 fix announcement XSS vulnerability#2

Open
BBleae wants to merge 6 commits intoTransmtfTeam:mainfrom
BBleae:xss-fix
Open

🐛 fix announcement XSS vulnerability#2
BBleae wants to merge 6 commits intoTransmtfTeam:mainfrom
BBleae:xss-fix

Conversation

@BBleae
Copy link
Copy Markdown
Contributor

@BBleae BBleae commented Feb 28, 2026

Summary

  • 引入 DOMPurify 对公告 HTML 做白名单净化,修复 dangerouslySetInnerHTML 场景下的 XSS 风险
  • 仅允许安全标签/属性,禁止 script/iframe/style/svg/math 等高风险标签与 style 属性
  • 对链接做二次安全收敛:
    • _blanktarget 会被移除
    • _blank 链接会强制带上 rel="noopener noreferrer"
  • URI 规则收紧为仅允许 httpsmailtotel、以及站内相对路径(并拒绝 // 协议相对 URL)
  • 对历史用户做兼容迁移:若本地存的是“旧版原文哈希”,会静默迁移到“新版净化后哈希”,避免一次性重弹

Review Feedback Addressed

  • 移除冗余类型依赖 @types/dompurify
  • 修复 reverse tabnabbing 风险
  • 处理“哈希策略切换导致旧用户重弹”的兼容问题
  • 收紧 URI 方案,避免 http:// 与协议相对 URL 偏离安全意图

Test Plan

  • npm run build 通过
  • 公告内容净化逻辑确定性用例(6 组)
  • 随机 fuzz 压测(12,000 轮)
  • 边界回归:修复 target="" 残留问题后复测通过

Copilot AI review requested due to automatic review settings February 28, 2026 14:02
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes an XSS vulnerability in the AnnouncementModal component by introducing DOMPurify to sanitize HTML content fetched from a remote announcement API before it is rendered via dangerouslySetInnerHTML.

Changes:

  • Adds DOMPurify as a runtime dependency and configures a strict allowlist (tags, attributes, and URI schemes) for HTML sanitization.
  • Sanitizes the announcement content before hashing and displaying it, and skips rendering if the sanitized result is empty.
  • Updates package-lock.json to reflect the new dependency tree.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/components/AnnouncementModal.tsx Adds sanitizeAnnouncementHtml using DOMPurify with a strict allowlist; now sanitizes content before hashing and setting state
package.json Adds dompurify (runtime) and @types/dompurify (redundant stub) as dependencies
package-lock.json Lockfile updated to include dompurify and the redundant @types/dompurify

Issues found:

  1. @types/dompurify is a deprecated no-op (package.json line 15): The package-lock.json itself flags it as deprecated: "This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed." It should be removed from package.json.

  2. Reverse tabnabbing via target attribute (AnnouncementModal.tsx line 16): target is included in ANNOUNCEMENT_ALLOWED_ATTR, meaning announcement authors (or a compromised API) can inject target="_blank" on links. Without rel="noopener noreferrer", the opened page can access window.opener and redirect the parent tab. Either remove target from the allowlist, or add a DOMPurify afterSanitizeAttributes hook to enforce rel="noopener noreferrer" on any target="_blank" link.

  3. One-time re-display for existing users (AnnouncementModal.tsx line 53, nit): The hash is now computed on sanitized content rather than raw content. Existing users who stored a hash of the raw announcement will see the current announcement one more time. This is a minor, one-time UX regression to be aware of.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread package.json Outdated
Comment thread src/components/AnnouncementModal.tsx
Comment thread src/components/AnnouncementModal.tsx Outdated
@BBleae
Copy link
Copy Markdown
Contributor Author

BBleae commented Feb 28, 2026

补充一下这次修复后的测试结果(针对 AnnouncementModal 的净化与链接安全逻辑):

  • 测试环境:在目录 /tmp/ 独立 clone 执行,避免污染当前工作树。
  • 测试对象:从 src/components/AnnouncementModal.tsx 直接提取并执行 sanitizeAnnouncementHtml / enforceSafeLinkTargets 逻辑(不是手写模拟)。
  • 覆盖方式:
    • 确定性用例 6 组
    • 随机 fuzz 12,000 轮
  • 关键断言:
    • target 仅允许安全形态(非 _blank 不得保留)
    • _blank 链接必须具备 rel=noopener noreferrer(若 target 被 sanitizer 去除同样视为安全)
    • javascript: / data: URI 不可存活
    • on* 事件属性、script/style 标签及 style 属性不可存活

测试过程中首次跑出了一个边界问题:target="" 会残留;已修复为先做 trim + lowercase 后判定,非 _blank 一律移除,并重新执行同强度测试全部通过。

结果:High-intensity sanitizer test passed(Deterministic=6, Fuzz=12000, Elapsed=112928ms),且 npm run build 通过。

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/components/AnnouncementModal.tsx Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 3 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@TransmtfTeam TransmtfTeam deleted a comment from chatgpt-codex-connector Bot Mar 1, 2026
@xzdmycbx
Copy link
Copy Markdown
Contributor

xzdmycbx commented Mar 1, 2026

@codex Please confirm if this feature is necessary and whether it could impact normal announcement behavior (e.g., announcements often contain buttons).

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex couldn't complete this request. Try again later.

@xzdmycbx
Copy link
Copy Markdown
Contributor

xzdmycbx commented Mar 1, 2026

@codex Please confirm if this feature is necessary and whether it could impact normal announcement behavior (e.g., announcements often contain buttons).

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex couldn't complete this request. Try again later.

Copy link
Copy Markdown
Contributor

@xzdmycbx xzdmycbx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now the announcement sanitizer strips style (FORBID_ATTR includes style, and it’s not in ALLOWED_ATTR). This breaks our existing announcement content that relies on inline styles to render “button-like” links (layout/spacing + CTA button appearance). We also have existing announcements with CTA/button jump behavior, and the current sanitization removes the necessary pieces so the CTAs degrade into plain text/unstyled links.

Please update the implementation to:

  1. Allow style attributes to pass through for announcement HTML (at least for a controlled set of tags like a, div, span, p, etc.), so our CTA buttons and layout are preserved.
  2. Ensure the announcement “button jump/redirect” behavior works as intended for existing content (e.g., the CTA links should still behave like buttons and remain usable after sanitization).

Important: keep the security guarantees. If you’re concerned about enabling arbitrary inline CSS, we can restrict allowed CSS properties (e.g., via DOMPurify hooks / a CSS allowlist) rather than forbidding style entirely. But as-is, this is a functional regression for announcements that include CTA buttons.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants